package org.example.util;


import org.example.bean.Cord;
import org.example.bean.GameMap;
import org.example.bean.JsonGamePos;
import org.example.enums.MapPosCalcType;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * {@code @Desc:地图工具类}
 *
 * @Author: yanghongjun
 * @Date: 2024/2/28
 **/
public class MapUtil {


    /**
     * 根据坐标计算坐标的唯一ID。
     *
     * @param x x坐标
     * @param y y坐标
     * @return 坐标的唯一ID
     */
    public static int calcIdByPos(int x, int y, MapPosCalcType type) {
        // 确保坐标在最大范围内
        int maxX = type.getMaxX();
        int maxY = type.getMaxY();
        if (x < -maxX || x > maxX || y < -maxY || y > maxY) {
            throw new IllegalArgumentException("坐标超出范围");
        }
        // 将x和y坐标转换为非负数
        int nonNegativeX = x + Math.abs(maxX) + 1;
        int nonNegativeY = y + Math.abs(maxY) + 1;
        // 计算唯一ID
        return nonNegativeY * (2 * Math.abs(maxX) + 2) + nonNegativeX;
    }

    /**
     * 根据唯一ID反推坐标。
     *
     * @param tileId 坐标的唯一ID
     * @return 格子的坐标（x, y）
     */
    public static JsonGamePos calcPosById(int tileId, MapPosCalcType type) {
        int maxX = type.getMaxX();
        int maxY = type.getMaxY();
        // 计算y坐标（需要注意整数除法会向下取整）
        int nonNegativeY = tileId / (2 * Math.abs(maxX) + 2);
        // 从tileId中减去y坐标所占的部分，得到x坐标的偏移量
        int xOffset = tileId - nonNegativeY * (2 * Math.abs(maxX) + 2);
        // 将非负的x坐标转换为实际的x坐标
        int x = xOffset - Math.abs(maxX) - 1;
        // 将非负的y坐标转换为实际的y坐标
        int y = nonNegativeY - Math.abs(maxY) - 1;
        // 验证坐标是否在允许的范围内，如果不在，则抛出异常或返回null等
        if (x < -maxX || x > maxX || y < -maxY || y > maxY) {
            throw new IllegalArgumentException("无效的唯一ID，无法反推出坐标");
        }
        // 返回坐标的JsonGamePos对象
        return new JsonGamePos(x, y);
    }

    /**
     * 检查两个格子的唯一ID是否表示相邻的格子（上下左右）。
     *
     * @param tileId1 第一个格子的唯一ID
     * @param tileId2 第二个格子的唯一ID
     * @return 如果两个格子相邻，则返回true；否则返回false
     */
    public static boolean areTilesAdjacent(int tileId1, int tileId2, MapPosCalcType mapPosCalcType) {
        JsonGamePos pos1 = calcPosById(tileId1, mapPosCalcType);
        JsonGamePos pos2 = calcPosById(tileId2, mapPosCalcType);
        // 检查是否在同一行
        if (pos1.getZ() == pos2.getZ()) {
            // 在同一行上，检查x坐标是否相邻
            return Math.abs(pos1.getX() - pos2.getX()) == 1;
        }
        // 检查是否在同一列
        if (pos1.getX() == pos2.getX()) {
            // 在同一列上，检查y坐标是否相同
            return Math.abs(pos1.getZ() - pos2.getZ()) == 1;
        }
        // 不在同一行也不在同一列，则它们不相邻
        return false;
    }



    /**
     * 计算并返回从给定坐标(x, y)开始的矩形区域内所有格子的区域ID集合。
     *
     * @param width    矩形区域的宽度
     * @param height   矩形区域的高度
     * @param x        矩形区域的起始横坐标
     * @param y        矩形区域的起始纵坐标
     * @param calcType 坐标到区域ID的计算类型
     * @return 包含所有格子区域ID的HashSet集合
     */
    public static HashSet<Integer> calculateAreaIdsForRectangle(int width, int height, int x, int y, MapPosCalcType calcType) {
        HashSet<Integer> areaIds = new HashSet<>();
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int areaId = calcIdByPos(x + i, y + j, calcType);
                areaIds.add(areaId);
            }
        }
        return areaIds;
    }


    /**
     * 优化路径，通过去除不必要的节点来简化路径。
     *
     * @param path 原始路径
     * @param map  地图数据
     * @return 优化后的路径
     */
    public static List<JsonGamePos> optimizePath(List<Cord> path, GameMap map) {
        if (path == null || path.isEmpty() || map == null) {
            return null;
        }
        if (path.size() >= 3) {
            List<Cord> optimizedPath = new ArrayList<>();
            // 起点总是加入优化路径
            optimizedPath.add(path.get(0));
            Cord lastOptimizedNode = path.get(0);
            for (int i = 1; i < path.size() - 1; i++) {
                Cord currentNode = path.get(i);
                Cord nextNode = path.get(i + 1);
                // 检查当前节点是否可以与下一个节点直线相连而不穿过障碍物
                if (MapUtil.canConnectStraight(lastOptimizedNode.getX(),lastOptimizedNode.getY(), nextNode.getX(),nextNode.getY(), map.getMaps())) {
                    // 如果可以直线相连，则跳过当前节点
                    continue;
                }
                // 否则，将当前节点添加到优化路径中
                optimizedPath.add(currentNode);
                lastOptimizedNode = currentNode;
            }
            // 终点总是加入优化路径
            optimizedPath.add(path.get(path.size() - 1));
            path = optimizedPath;
        }
        List<JsonGamePos> posList = new ArrayList<>();
        for (Cord lazyNode : path) {
            JsonGamePos jsonGamePos = map.mapToGameCoords(lazyNode.getX(), lazyNode.getY());
            posList.add(jsonGamePos);
        }
        return posList;
    }


    /**
     * 检查两个节点是否可以直线相连而不穿过障碍物。
     * @param x1 起点x
     * @param y1 起点y
     * @param x2 终点x
     * @param y2 终点y
     * @param grid 地图网格
     * @return 如果两点之间可以直线相连并且没有障碍物则返回true，否则返回false
     */
    public static boolean canConnectStraight(int x1, int y1, int x2, int y2,int[][] grid) {
        int dx = Math.abs(x2 - x1);
        int dy = Math.abs(y2 - y1);
        int sx = (x1 < x2) ? 1 : -1;
        int sy = (y1 < y2) ? 1 : -1;
        int err = dx - dy;
        while (true) {
            if (x1 >= 0 && x1 < grid.length && y1 >= 0 && y1 < grid[0].length && grid[x1][y1] != 0) {
                // 路径上有障碍物
                return false;
            }
            if (x1 == x2 && y1 == y2) {
                break; // 到达终点
            }
            int e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
        }
        // 路径上无障碍物
        return true;
    }


}
